package edu.pku.sei.modeler.uml.commands;

import java.util.List;

import org.eclipse.gef.commands.Command;
import org.eclipse.jface.dialogs.MessageDialog;

import edu.pku.sei.modeler.model.core.MetaModel;
import edu.pku.sei.modeler.model.core.MetaModelElement;
import edu.pku.sei.modeler.model.core.MetaNamedElement;
import edu.pku.sei.modeler.model.core.MetaPackage;
import edu.pku.sei.modeler.model.structure.MetaDiagram;
import edu.pku.sei.modeler.model.structure.MetaNode;
import edu.pku.sei.modeler.uml.util.ModelHandler;

public class UMLElementDeleteCommand extends Command {

	protected MetaNode node;

	protected MetaDiagram diagram;

	protected boolean isDeletedFromDiagram;
	
	private boolean isDragOperation = false;

	public UMLElementDeleteCommand(MetaNode node, MetaDiagram diagram,
			boolean isDeletedFromDiagram) {
		super("Delete UML element");
		this.node = node;
		this.diagram = diagram;

		this.isDeletedFromDiagram = isDeletedFromDiagram;
	}
	
	public UMLElementDeleteCommand(MetaNode node, MetaDiagram diagram,
			boolean isDeletedFromDiagram, boolean isDragOperation) {
		super("Delete UML element");
		this.node = node;
		this.diagram = diagram;

		this.isDeletedFromDiagram = isDeletedFromDiagram;
		this.isDragOperation = isDragOperation;
	}

	public void setDragOperation(boolean isDragOperation) {
		this.isDragOperation = isDragOperation;
	}
	
	@Override
	public boolean canExecute() {
		return this.node != null && this.diagram != null;
	}

	@Override
	public boolean canUndo() {
		return this.node != null && this.diagram != null;
	}

	@Override
	public void execute() {
		boolean flag = ModelHandler
				.isMetaElementExistedinMoreThenOneDiagram(node
						.getModelElement());
		if ((!isDragOperation) && (!isDeletedFromDiagram) && flag) {
			MessageDialog
					.openError(null, "Deleting failure",
							"It still exists in other diagram(s). It cannot be deleted.");
			return;
		}
		deleteGraphNode();

		if (!isDeletedFromDiagram) {
			deleteModelElement();
		}
	}

	protected void deleteGraphNode() {
		diagram.getNodes().remove(node);
		// node.setDiagram(null);
	}

	protected void deleteModelElement() {
		MetaModel model = (MetaModel) diagram.getModel();
		MetaModelElement modelElement = node.getModelElement();
		model.removeFromModel(modelElement);

		MetaPackage pkg = findPackage(model, diagram);
		if (pkg != null) {
			if (modelElement instanceof MetaPackage) {
				pkg.getSubPackages().remove(modelElement);
			} else if (modelElement instanceof MetaNamedElement)
				pkg.getNamedElements().remove(modelElement);
			else
				pkg.getOtherElements().remove(modelElement);
		}

	}

	@Override
	public void undo() {
		node.setDiagram(diagram);
		node.setModel(diagram.getModel());

		// overrided by subclasses
		undoDeleteGraphNode();

		if (!isDeletedFromDiagram) {
			MetaModel model = (MetaModel) diagram.getModel();
			MetaModelElement modelElement = this.node.getModelElement();
			modelElement.setModel(model);
			model.registerType(modelElement);

			if (node.isReferenceShape())
				return;

			// overrided by subclasses
			undoDeleteModelElement();

		}

	}

	protected void undoDeleteModelElement() {
		MetaModelElement modelElement = node.getModelElement();
		MetaModel model = (MetaModel) diagram.getModel();
		MetaPackage pkg = findPackage(model, diagram);
		if (pkg != null) {
			if (modelElement instanceof MetaPackage) {
				pkg.getSubPackages().add(modelElement);
			} else if (modelElement instanceof MetaNamedElement)
				pkg.getNamedElements().add(modelElement);
			else
				pkg.getOtherElements().add(modelElement);
		}

	}

	protected void undoDeleteGraphNode() {
		diagram.getNodes().add(node);

	}

	protected MetaPackage findPackage(MetaModel model, MetaDiagram diagram) {
		if (model == null || diagram == null)
			return null;
		List pkgs = model.getPackages();
		for (int i = 0; i < pkgs.size(); i++) {
			MetaPackage pkg = (MetaPackage) pkgs.get(i);
			MetaPackage result = findPackage(pkg, diagram);
			if (result != null)
				return result;
		}
		return null;
	}

	protected MetaPackage findPackage(MetaPackage pkg, MetaDiagram diagram) {
		if (pkg == null)
			return null;
		List diags = pkg.getDiagrams();
		for (int i = 0; i < diags.size(); i++) {
			if (diags.get(i) == diagram)
				return pkg;
		}
		List pkgs = pkg.getSubPackages();
		for (int i = 0; i < pkgs.size(); i++) {
			MetaPackage subPkg = (MetaPackage) pkgs.get(i);
			MetaPackage result = findPackage(subPkg, diagram);
			if (result != null)
				return result;
		}
		return null;
	}
}
